﻿
#pragma once

namespace tools{
    namespace sfinae{

        struct YesNo  { typedef char (&No)[1]; typedef char (&Yes)[2]; };
        
        template<class T, class U> struct Same { typedef YesNo::No  Result; };
        template<class T> struct Same<T,T>     { typedef YesNo::Yes Result; };

        template<class C, class T> struct Base{};

        template<class C, class R> struct Base<C, R()>
        {
            typedef R(C::*Method)();
            typedef R(C::*CMethod)()const;
            
        };

        template<class C, class R,class A1> struct Base<C, R(A1)>
        {
            typedef R(C::*Method)(A1);
            typedef R(C::*CMethod)(A1)const;
        };
        template<class C, class R,class A1,class A2> struct Base<C, R(A1,A2)>
        {
            typedef R(C::*Method)(A1,A2);
            typedef R(C::*CMethod)(A1,A2)const;
        };
        template<class C, class R,class A1,class A2,class A3> struct Base<C, R(A1,A2,A3)>
        {
            typedef R(C::*Method)(A1,A2,A3);
            typedef R(C::*CMethod)(A1,A2,A3)const;
        };

        template<class C, class T> struct Base1: Base<C, T>,  YesNo
        {
            template< typename Base<C, T>::Method > struct Helper; 
            template< typename Base<C, T>::CMethod> struct CHelper;
            template<class U>static No CheckConst(...); 
            template<class U>static No CheckNoConst(...);
        };
        

    
    }//namespace sfinae
}//namespace tools


#define dDEDUCE_TYPE(member_name) \
    decltype(std::declval<U>().member_name)

#define dDETECTOR_MEMBER_DATA(member_name)                            \
template<class C,class T,    \
    size_t IS_FUNCTION = ::std::is_function<T>::value  \
> struct has_##member_name                    \
    : ::tools::sfinae::YesNo                                          \
{                                                                     \
    template<class U>static typename ::tools::sfinae::                \
        Same<T, dDEDUCE_TYPE(member_name) >::Result                   \
            Check( dDEDUCE_TYPE(member_name)* );                      \
                                                                      \
    template<class U>static No Check(...);                            \
                                                                      \
    enum { eIS_AVAILABLE = sizeof(Check<C>(0)) == sizeof(Yes) };      \
};

#define dDETECTOR_MEMBER_FUNCTION(member_name)                        \
template< class C, class T> struct has_##member_name<C,T,1>           \
    : ::tools::sfinae::Base1<C,T>                                     \
{                                                                     \
    template<class U>static                                           \
        Yes CheckNoConst( Helper< &U::member_name >* p);              \
                                                                      \
    template<class U>static                                           \
        Yes CheckConst( CHelper< &U::member_name >* p);               \
                                                                      \
    enum { eIS_NOCONST = sizeof(CheckNoConst<C>(0)) == sizeof(Yes) }; \
    enum { eIS_CONST   = sizeof(CheckConst<C>(0))   == sizeof(Yes) }; \
    enum { eIS_AVAILABLE = eIS_NOCONST || eIS_CONST };                \
};

#define DETECTED_MEMBER(member_name)                                  \
dDETECTOR_MEMBER_DATA(member_name)                                    \
dDETECTOR_MEMBER_FUNCTION(member_name)                                

#if 0
#define dDEDUCE_TYPE(member_name) \
    decltype(std::declval<U>().member_name)


#define dDETECTOR_GENERAL_TEMPLATE(member_name)                       \
template<class C,class T> struct has_##member_name                    \
    : ::tools::sfinae::YesNo                                          \
{                                                                     \
    template<class U>static typename ::tools::sfinae::                \
        Same<T, dDEDUCE_TYPE(member_name) >::Result                   \
            Check( dDEDUCE_TYPE(member_name)* );                      \
                                                                      \
    template<class U>static No Check(...);                            \
                                                                      \
    enum { eIS_AVAILABLE = sizeof(Check<C>(0)) == sizeof(Yes) };      \
};


#define dDETECTOR_TEMPLATE_HEADER(member_name, ...) \
    template< class C, class R, __VA_ARGS__ > 

#define dDETECTOR_TEMPLATE_BODY(member_name, ...)                     \
struct has_##member_name<C,R(__VA_ARGS__)>                            \
        : ::tools::sfinae::YesNo                                      \
{                                                                     \
    typedef R(C::*Method)(__VA_ARGS__);                               \
    typedef R(C::*CMethod)(__VA_ARGS__)const;                         \
                                                                      \
    template<Method > struct Helper;                                  \
    template<CMethod> struct CHelper;                                 \
                                                                      \
    template<class U>static                                           \
        Yes CheckNoConst( Helper< &U::member_name >* p);              \
                                                                      \
    template<class U>static                                           \
        Yes CheckConst( CHelper< &U::member_name >* p);               \
                                                                      \
    template<class U>static No CheckConst(...);                       \
    template<class U>static No CheckNoConst(...);                     \
                                                                      \
    enum { eIS_NOCONST = sizeof(CheckNoConst<C>(0)) == sizeof(Yes) }; \
    enum { eIS_CONST   = sizeof(CheckConst<C>(0))   == sizeof(Yes) }; \
    enum { eIS_AVAILABLE = eIS_NOCONST || eIS_CONST };                \
};                                                                  

#define DETECTED_MEMBER(member_name)                                  \
dDETECTOR_GENERAL_TEMPLATE(member_name)                               \
dDETECTOR_TEMPLATE_HEADER(member_name)                                \
dDETECTOR_TEMPLATE_BODY(member_name)                                  \
dDETECTOR_TEMPLATE_HEADER(member_name,class A1)                       \
dDETECTOR_TEMPLATE_BODY(member_name, A1)                              \
dDETECTOR_TEMPLATE_HEADER(member_name,class A1,class A2)              \
dDETECTOR_TEMPLATE_BODY(member_name, A1,A2)                           \
dDETECTOR_TEMPLATE_HEADER(member_name,class A1,class A2,class A3)     \
dDETECTOR_TEMPLATE_BODY(member_name, A1,A2,A3)                       
#endif

//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------

#if 0
#define dDETECTOR_GENERAL_TEMPLATE(member_name)                     \
template<class C,class T> struct has_##member_name                  \
{                                                                   \
    typedef char (&No)[1]; typedef char (&Yes)[2];                  \
    template<class U>static                                         \
        typename ::std::conditional<                                \
            ::std::is_same<                                         \
                T, decltype(std::declval<U>().member_name )         \
            >::value, Yes, No                                       \
        >::type                                                     \
        Check( decltype(std::declval<U>().member_name )* );         \
    template<class U>static No Check(...);                          \
    enum { eIS_AVAILABLE = sizeof(Check<C>(0)) == sizeof(Yes) };    \
};                                                                  

#define dDETECTOR_TEMPLATE_HEADER(member_name, ...) \
    template< class C, class R, __VA_ARGS__ > 

#define dDETECTOR_TEMPLATE_BODY(member_name, ...)                           \
struct has_##member_name<C,R(__VA_ARGS__)>                                  \
{                                                                           \
    typedef char (&No)[1]; typedef char (&Yes)[2];                          \
                                                                            \
    typedef R(C::*Method)(__VA_ARGS__);                                     \
    typedef R(C::*CMethod)(__VA_ARGS__)const;                               \
                                                                            \
    template<R(C::*)(__VA_ARGS__)      > struct Helper;                     \
    template<R(C::*)(__VA_ARGS__)const > struct CHelper;                    \
                                                                            \
    template<class U>static                                                 \
        Yes CheckNoConst( Helper<static_cast<Method>(&U::member_name)>* p); \
                                                                            \
    template<class U>static                                                 \
        Yes CheckConst( CHelper<static_cast<CMethod>(&U::member_name)>* p); \
                                                                            \
    template<class U>static No CheckConst(...);                             \
    template<class U>static No CheckNoConst(...);                           \
                                                                            \
    enum { eIS_NOCONST = sizeof(CheckNoConst<C>(0)) == sizeof(Yes) };       \
    enum { eIS_CONST   = sizeof(CheckConst<C>(0))   == sizeof(Yes) };       \
    enum { eIS_AVAILABLE = eIS_NOCONST || eIS_CONST };                      \
};                                                                  

#define DETECTED_MEMBER(member_name)                                \
dDETECTOR_GENERAL_TEMPLATE(member_name)                             \
dDETECTOR_TEMPLATE_HEADER(member_name)                              \
dDETECTOR_TEMPLATE_BODY(member_name)                                \
dDETECTOR_TEMPLATE_HEADER(member_name,class A1)                     \
dDETECTOR_TEMPLATE_BODY(member_name, A1)                            \
dDETECTOR_TEMPLATE_HEADER(member_name,class A1,class A2)            \
dDETECTOR_TEMPLATE_BODY(member_name, A1,A2)                         \
dDETECTOR_TEMPLATE_HEADER(member_name,class A1,class A2,class A3)   \
dDETECTOR_TEMPLATE_BODY(member_name, A1,A2,A3)                       
#endif


#define is_available(class_name, member_type, member_name)          \
    has_##member_name<class_name, member_type>::eIS_AVAILABLE
#define is_available_method(class_name, member_type, member_name)   \
    has_##member_name<class_name, member_type>::eIS_NOCONST
#define is_available_cmethod(class_name, member_type, member_name)  \
    has_##member_name<class_name, member_type>::eIS_CONST


